/*
 * =====================================================================================
 *
 *       Filename:  cpu.c
 *
 *    Description:
 *
 *        Version:  1.0
 *        Created:  2025/03/17 16时12分10秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  zhuzonghui(puchuchen@outlook.com) (),
 *   Organization:
 *
 * =====================================================================================
 */
#include <fuquOS/cpu.h>
#include <fuquOS/stdio.h>

cpu_info_t *cpu_info = NULL;

void cpuid(u32 eax, u32 ecx, u32 *a, u32 *b, u32 *c, u32 *d)
{
    asm volatile("cpuid"
                 : "=a"(*a), "=b"(*b), "=c"(*c), "=d"(*d)
                 : "a"(eax), "c"(ecx));
}

void cpu_init()
{
    cpu_info_t *info = (cpu_info_t *)malloc(sizeof(cpu_info_t));

    int eax, ebx, ecx, edx;
    cpuid(0, 0, &eax, &ebx, &ecx, &edx);

    ((int *)info->vendor)[0] = ebx;
    ((int *)info->vendor)[1] = edx;
    ((int *)info->vendor)[2] = ecx;
    info->vendor[12] = '\0';

    // 获取 CPU 型号
    cpuid(0x80000002, 0, &eax, &ebx, &ecx, &edx);
    ((int *)info->model)[0] = eax;
    ((int *)info->model)[1] = ebx;
    ((int *)info->model)[2] = ecx;
    ((int *)info->model)[3] = edx;

    cpuid(0x80000003, 0, &eax, &ebx, &ecx, &edx);
    *(u32 *)(info->model + 16) = eax;
    *(u32 *)(info->model + 20) = ebx;
    *(u32 *)(info->model + 24) = ecx;
    *(u32 *)(info->model + 28) = edx;

    cpuid(0x80000004, 0, &eax, &ebx, &ecx, &edx);
    *(u32 *)(info->model + 32) = eax;
    *(u32 *)(info->model + 36) = ebx;
    *(u32 *)(info->model + 40) = ecx;
    *(u32 *)(info->model + 44) = edx;
    info->model[48] = '\0';

    // 获取 CPU 频率
    cpuid(0x16, 0, &eax, &ebx, &ecx, &edx);
    info->frequency = eax; // 基础频率 (MHz)

    // 获取逻辑处理器数
    cpuid(1, 0, &eax, &ebx, &ecx, &edx);
    info->thread_count = (ebx >> 16) & 0xFF; // 逻辑处理器数

    // 获取物理核心数
    cpuid(4, 0, &eax, &ebx, &ecx, &edx);
    info->core_count = ((eax >> 26) & 0x3F) + 1; // 物理核心数

    // 获取 CPU 特性
    info->feature_count = 0;

    if (edx & (1 << 0))
        info->features[info->feature_count++] = 0x01; // FPU
    if (edx & (1 << 3))
        info->features[info->feature_count++] = 0x02; // PSE
    if (edx & (1 << 4))
        info->features[info->feature_count++] = 0x03; // TSC
    if (edx & (1 << 6))
        info->features[info->feature_count++] = 0x04; // PAE
    if (edx & (1 << 9))
        info->features[info->feature_count++] = 0x05; // APIC
    if (ecx & (1 << 0))
        info->features[info->feature_count++] = 0x06; // SSE3
    if (ecx & (1 << 9))
        info->features[info->feature_count++] = 0x07; // SSSE3
    if (ecx & (1 << 19))
        info->features[info->feature_count++] = 0x08; // SSE4.1
    if (ecx & (1 << 20))
        info->features[info->feature_count++] = 0x09; // SSE4.2

    cpu_info = info;
}

cpu_info_t *get_cpu_info()
{
    return cpu_info;
}